package com.zhoug.encrypt3;

import android.util.Base64;


import com.zhoug.logger3.Logger;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

/**
 * base64编码工具
 *
 * @Author 35574
 * @Date 2020/12/18
 * @Description
 */
public class Base64Utils {
    private static final String TAG = ">>>Base64Utils";
    private static final boolean DEBUG = false;

    /**
     * 字符串编码方式
     */
    private Charset charset = StandardCharsets.UTF_8;

    /**
     * 编码和解码的flag
     * {@link Base64#DEFAULT} :最后有个换行符\n
     * {@link Base64#NO_PADDING} :这个参数是略去加密字符串最后的”=”
     * {@link Base64#NO_WRAP} :这个参数意思是略去所有的换行符
     */
    private int flag = Base64.NO_WRAP;
    private static final int LINE_GROUPS = 19;


    public Base64Utils() {
    }

    public Base64Utils(Charset chartSet, int flag) {
        if (chartSet != null) {
            this.charset = chartSet;
        }
        this.flag = flag;
    }

    /**
     * 编码
     *
     * @param data 编码之前的字节数据
     * @return 编码之后的字节数组
     */
    public byte[] encode(byte[] data) {
        if (data != null && data.length > 0) {
            return Base64.encode(data, flag);

        }
        return data;
    }

    /**
     * 编码
     *
     * @param data 编码之前的字符串
     * @return 编码之后的字节数组
     */
    public byte[] encode(String data) {
        if (data != null) {
            return encode(getBytes(data));
        }
        return null;
    }

    /**
     * 编码为base64字符串
     *
     * @param data 编码之前的字节数据
     * @return 编码之后的base64字符串
     */
    public String encodeToString(byte[] data) {
        if (data == null || data.length == 0) return null;

        return Base64.encodeToString(data, 0, data.length, flag);
    }

    /**
     * 编码为base64字符串
     *
     * @param data 编码之前的字符串
     * @return 编码之后的base64字符串
     */
    public String encodeToString(String data) {
        if (data == null) return null;
        return encodeToString(getBytes(data));
    }


    /**
     * base64字符串解码为字节数组
     *
     * @param base64String 解码之前的base64字符串
     * @return 解码之后的字节数组
     */
    public byte[] decode(String base64String) {
        if (base64String == null) return null;
        return Base64.decode(base64String, flag);
    }

    /**
     * 解码为字节数组
     *
     * @param data 解码之前的字节数组
     * @return 解码之后的字节数组
     */
    public byte[] decode(byte[] data) {
        if (data == null) return null;
        return Base64.decode(data, flag);
    }

    /**
     * base64字符串解码
     *
     * @param base64String 解码之前的base64字符串
     * @return 解码之后的字符串
     */
    public String decodeToString(String base64String) {
        if (base64String != null) {
            byte[] decode = decode(base64String);
            if (decode != null && decode.length > 0) {
                return getString(decode);

            }

        }
        return null;
    }

    /**
     * base64字符串解码
     *
     * @param data 解码之前的字节数组
     * @return 解码之后的字符串
     */
    public String decodeToString(byte[] data) {
        if (data != null && data.length > 0) {
            byte[] decode = decode(data);
            if (decode != null && decode.length > 0) {
                return getString(decode);

            }
        }
        return null;
    }

    /**
     * 判断字符串是否被base64编码
     *
     * @param data
     * @return
     */
    public boolean isBase64Encoded(String data) {
        try {
            if (data == null || data.length() == 0) return false;
            //先对字符串进行解密，再对解密后的内容进行加密。如果加密后的内容和传过来的值相同则就是加密过的。
            //原因：因为内容如果是加密的解密再加密就能回到原来内容。
            // 不是加密的，解密就会出现乱码，再进行加密自然不会和以前一样
            String s = decodeToString(data);//原文
            String s1 = encodeToString(s);//原文加密后
            if (s1 != null && data.equals(s1.trim())) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 如果字符串是被base64加密后的,那么解密后返回正确的值,如果没有加密则直接返回
     *
     * @param str
     * @return
     */
    public String decryptIfEncrypt(String str) {
        if (str == null) return null;
        //先对字符串进行解密，再对解密后的内容进行加密。如果加密后的内容和传过来的值相同则就是加密过的。
        //原因：因为内容如果是加密的解密再加密就能回到原来内容。
        // 不是加密的，解密就会出现乱码，再进行加密自然不会和以前一样
        try {
            if (DEBUG) {
                Logger.d(TAG, "decryptIfEncrypt:原数据str=" + str);
            }
            //先解密
            String decryptStr = decodeToString(str);
            if (DEBUG) {
                Logger.d(TAG, "decryptIfEncrypt:解密后decryptStr=" + decryptStr);
            }
            //对解密的数据再加密
            String str2 = encodeToString(decryptStr);
            if (DEBUG) {
                Logger.d(TAG, "decryptIfEncrypt:重新加密后str2=" + str2);
            }
            if (str2 != null) {
                //最后可能有个换行符
                if (str2.endsWith("\n")) {
                    str2 = str2.substring(0, str2.length() - 1);
                }
                if (str.equals(str2)) {
                    if (DEBUG) {
                        Logger.d(TAG, "decryptIfEncrypt:被加密");
                    }
                    //可能有多重加密
                    return decryptIfEncrypt(decryptStr);
                }
            }
        } catch (Exception e) {
            //解密错误,表示没有被加密
            return str;
        }
        return str;
    }

    /**
     * 字符串转化为字节数组
     *
     * @param str
     * @return
     */
    public byte[] getBytes(String str) {
        if (str != null && str.length() > 0) {
            return str.getBytes(charset);
        }
        return null;
    }

    /**
     * 字节数组转化为字符串
     *
     * @param data
     * @return
     */
    public String getString(byte[] data) {
        if (data != null && data.length > 0) {
            return new String(data, 0, data.length, charset);
        }
        return null;
    }


    public Charset getCharset() {
        return charset;
    }

    public int getFlag() {
        return flag;
    }

    public int calculateSecretLength(int byteSize) {
        return calculateSecretLength(byteSize, flag);
    }


    /**
     * 计算秘文长度
     *
     * @param byteSize
     * @return
     */
    public static int calculateSecretLength(int byteSize, int flags) {
        if (byteSize == 0) {
            return 0;
        }
        boolean do_padding = (flags & Base64.NO_PADDING) == 0;
        boolean do_newline = (flags & Base64.NO_WRAP) == 0;
        boolean do_cr = (flags & Base64.CRLF) != 0;
        //秘文长度
        int outputLen = byteSize / 3 * 4;
        if (do_padding) {
            if (byteSize % 3 > 0) {
                outputLen += 4;
            }
        } else {
            switch (byteSize % 3) {
                case 0:
                    break;
                case 1:
                    outputLen += 2;
                    break;
                case 2:
                    outputLen += 3;
                    break;
            }
        }

        // Account for the newlines, if any.
        if (do_newline && byteSize > 0) {
            outputLen += (((byteSize - 1) / (3 * LINE_GROUPS)) + 1) *
                    (do_cr ? 2 : 1);
        }

        return outputLen;
    }

    public static int calculateSecretLength(byte[] data, int flags) {
        return calculateSecretLength(data == null ? 0 : data.length, flags);
    }

    public static int calculateSecretLength(String data, int flags) {
        return calculateSecretLength(data == null ? 0 : data.getBytes().length, flags);
    }
}
